# encoding: utf-8
"""
解释器：解释指令集并执行
注：每条指令必须大写
"""
import os
import sys
import pickle

from Compiler.Compiler import Compiler
from Error.Error import send_error
from share import ver, nums, what_type, types
from loader.codes_loader import codes_dict
from loader.func_loader import num_func
from loader.class_loader import build_class
from TVM import TRE

from .Object.function import function
from .Object.module import Module
from .types.array import array
from .types.string import string
from .built_in import built_in_func


class Interpreter:
    """解释器"""

    def __init__(self, codes=None, path=None):
        # 数据栈
        self.stack = []
        # 函数
        self.mfunc = {}
        # 类
        self.mclass = {}
        # 类型
        self.classes = types
        if codes:
            # 文件全部内容
            self.file = codes
            # 版本号
            self.ver = self.file[0]
            # 指令集
            try:
                self.codes = [
                    [(codes_dict[j[0]], j[1]) for j in i] for i in self.file[1]
                ]
            except KeyError:
                send_error("RunError", f"there is a bytecode can't be run.")
            # 值
            self.values = self.file[2]
            # 名字
            self.names = self.file[3]
            self.classes.update(self.file[4])
        else:
            self.ver = ver
        # 文件路径
        self.path = path
        # 是否在执行函数
        self.run_func = False
        # 正在执行的函数
        self.func = None
        # 调用自己的模块，在程序结束时切换到调用者
        self.parent = "__main__"
        if self.path:
            # 切换到相应目录
            c_path = os.path.dirname(os.path.abspath(self.path))
            os.chdir(c_path)
        self.name = "__main__"
        # 全局变量(内置一些变量)
        self.environment = {
            "__name__": ("__main__", "string"),
            "__file__": (path, "srting"),
        }
        # 虚拟机拒绝解析比自己版本高的ctree文件
        if self.ver > ver:
            send_error("VersionError", self.ver, ver)
            sys.exit()
        TRE.run_index["__main__"] = 0
        # 模块
        self.module = {"__main__": self}

    def push(self, v):
        """入数据栈"""
        self.stack.append(v)

    def pop(self):
        """出数据栈"""
        try:
            return self.stack.pop(-1)
        except IndexError:
            send_error("RunError", "Stack in empty.")

    def top(self):
        """弹出栈顶"""
        try:
            return self.stack[-1]
        except IndexError:
            send_error("RunError", "Stack in empty.")

    # -------------------------------- 指令集定义开始 --------------------------------------
    def ADD(self):
        """两数相加"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first + second)
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't add.",
            )

    def SUB(self):
        """两数相减"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first - second)
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't sub.",
            )

    def MUL(self):
        """两数相乘"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first * second)
        except TypeError:
            send_error(
                "TypeError",
                f'"{type(first).__name__}" and "{type(second).__name__}" couldn\'t mul.',
            )

    def DIV(self):
        """两数相除"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first / second)
        except ZeroDivisionError:
            send_error("ZeroDivError", str(first))
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't div.",
            )

    def ZDIV(self):
        """整除"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first // second)
        except ZeroDivisionError:
            send_error("ZeroDivError", str(first))
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't div.",
            )

    def POW(self):
        """两数乘方"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first ** second)
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't pow.",
            )

    def MOD(self):
        """模运算"""
        second = self.pop()
        first = self.pop()
        try:
            self.push(first % second)
        except ZeroDivisionError:
            send_error("ZeroDivError", str(first))
        except TypeError:
            send_error(
                "TypeError",
                f"{type(first).__name__} and {type(second).__name__} couldn't mod.",
            )

    def LOAD_VALUE(self, number):
        """入栈"""
        self.push(number)

    def STORE_NAME(self, name):
        """从栈顶读取值并生成变量"""
        val = self.pop()
        self.environment[name] = (val, type(val).__name__)

    def LOAD_NAME(self, name):
        """取出变量的值并入栈"""
        try:
            val = self.environment[name][0]
        except KeyError:
            send_error("NameError", name)
        self.push(val)

    def CHANGE_VALUE(self, name):
        """修改变量的值"""
        value = self.pop()
        try:
            type_ = self.environment[name][1]
        except KeyError:
            send_error("NameError", name)
        else:
            try:
                value = self.classes[type_](value)
            except ValueError:
                send_error("ValueError", value, type_)
            self.environment[name] = (value, type_)

    def POP_VALUE(self):
        """取出栈里的值并抛弃"""
        self.pop()

    def IMPORT(self):
        """导入模块，执行导入模块并分配命名空间"""
        # 模块名称
        importpath_code = string(self.pop())
        # 获取目标名字（不带后缀），用于使用模块中的成员，将它的名字添加到列表中
        importname = os.path.splitext(os.path.split(importpath_code)[1])[0]
        # 把.替换成/路径符号并加上后缀，生成目标模块路径
        tree_importpath = os.path.abspath(importpath_code.replace(".", "/") + ".tree")
        ctree_importpath = os.path.abspath(importpath_code.replace(".", "/") + ".ctree")
        # 进行判断，使ctree或tree都可执行
        if os.path.isfile(tree_importpath):
            # tree
            try:
                with open(tree_importpath, encoding="UTF-8") as file:
                    # 运行时编译为指令
                    import_codes = Compiler(file.read())
            except FileNotFoundError:
                send_error("ModuleNotFoundError", importname)
            key = tree_importpath
        else:
            # ctree
            try:
                with open(ctree_importpath, "rb") as file:
                    # 运行时编译为指令
                    import_codes = pickle.loads(file.read())
            except FileNotFoundError:
                send_error("ModuleNotFoundError", importname)
            except EOFError:
                send_error("RunError", f"Module '{importname}' is empty.")
            key = ctree_importpath
        module = Module(import_codes, key)
        self.module[key] = module
        # 名称引用
        self.names.append(importname)
        self.run_code(object_=key)

    def JUMP_IF_FALSE(self):
        """如果条件不成立则跳转"""
        cond = self.pop()
        cond = int(bool(cond))
        if not cond:
            self.GOTO()
        else:
            self.POP_VALUE()

    def GOTO(self):
        """跳转到指定的字节码索引"""
        TRE.run_index[TRE.TRE_module] = int(self.pop())

    def EQUAL(self):
        """判断两个数是否相等"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        # 类型强制转化
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 == condit2))

    def UNEQUAL(self):
        """判断两个数是否不相等"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 != condit2))

    def GREATER(self):
        """判断一个数是否大于另一个数"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 > condit2))

    def LESS(self):
        """判断一个数是否小于另一个数"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 < condit2))

    def LESS_EQUAL(self):
        """判断一个数是否小于等于另一个数"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 <= condit2))

    def GREATER_EQUAL(self):
        """判断一个数是否大于等于另一个数"""
        condit2 = self.pop()
        condit1 = self.pop()
        cond_type = type(condit2).__name__
        condit1 = self.classes[cond_type](condit1)
        self.push(int(condit1 >= condit2))

    def NOP(self):
        """这个指令什么也不做，通常用于注释及}"""
        pass

    def DUP(self):
        """复制栈顶"""
        self.push(self.top())

    def SWAP(self):
        """交换栈顶的两个值"""
        a = self.pop()
        b = self.pop()
        self.push(a)
        self.push(b)

    def FUNC(self):
        """建立函数环境"""
        func_argument_len = self.pop()
        func_arguments = [self.pop() for i in range(func_argument_len)]
        func_name = self.pop()
        self.mfunc[func_name] = function(name=func_name, arguments=func_arguments)

    def RUN_FUNC(self):
        """执行自定义函数"""
        funcname = self.pop()
        self.func = funcname
        self.run_func = True
        self.mfunc[funcname]()
        self.run_func = False

    def RETURN_VALUE(self):
        """函数返回值"""
        # TODO
        pass

    def ARRAY(self):
        """创建数组"""
        long = self.pop()
        name = self.pop()
        data_long = self.pop()
        data_list = []
        for i in range(data_long):
            data_list.append(self.pop())
        self.environment[name] = (array(long, data_list), "array")

    def ARRAY_VALUE(self):
        """取出数组或列表的值"""
        index = self.pop()
        name = self.pop()
        self.push(self.environment[name][0][index])

    def DEL(self):
        """删除变量"""
        name = self.pop()
        try:
            del self.environment[name]
        except KeyError:
            send_error("NameError", name)

    def ASSERT(self):
        """调试指令：断言"""
        condit = self.pop()
        if not condit:
            send_error("AssertError", "assert")

    def BUILTIN_FUNC(self):
        """执行内置函数"""
        funcname = self.pop()
        arguments_long = self.pop()
        try:
            funcname = num_func[funcname].upper()
            func = getattr(built_in_func, funcname)
        except KeyError:
            send_error("NameError", f"{funcname.lower()}")
        arguments_list = [self.pop() for i in range(arguments_long)]
        self.push(func(arguments_list))

    def CLASS(self):
        """建造新类"""
        classname = self.pop()
        class_ = build_class(classname)
        class_locals_var_num = self.pop()
        class_global_var_num = self.pop()
        class_locals_method_num = self.pop()
        class_global_method_num = self.pop()
        # 生成一个类
        class_[1][0] = (self.pop() for i in range(class_locals_var_num))
        class_[1][1] = (self.pop() for i in range(class_global_var_num))
        class_[2][0] = (self.pop() for i in range(class_locals_method_num))
        class_[2][1] = (self.pop() for i in range(class_global_method_num))
        self.mclass[classname] = class_

    # -------------------------------- 指令集定义结束 --------------------------------------
    def parse_argument(self, module_name, code, argument):
        """根据语句取值传参"""
        numbers = ("LOAD_VALUE",)
        names = ("LOAD_NAME", "STORE_NAME", "CHANGE_VALUE")
        if code in numbers:
            return self.module[module_name].values[argument]
        elif code in names:
            return self.module[module_name].names[argument]

    def run_code(self, object_="__main__"):
        """虚拟机执行引擎"""
        codes = self.module[object_].codes
        module_name = self.module[object_].name
        parent = self.module[object_].parent
        stack = self.module[object_].stack

        TRE.TRE_module = module_name
        TRE.run_index[module_name] = 0

        while TRE.run_index[module_name] < len(codes):
            each = codes[TRE.run_index[module_name]]
            TRE.run_index[module_name] += 1

            for byte_code in each:
                code, argument = byte_code
                bytecode = getattr(self, code)
                if argument == -1:
                    bytecode()
                else:
                    bytecode(self.parse_argument(module_name, code, argument))
        # 父模块
        TRE.TRE_module = parent
        TRE.run_index[module_name] = 0
        stack.clear()
